package com.example.learn_gl_android.anglib.util

import android.content.res.Resources
import android.opengl.GLES30
import android.util.Log
import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer
import java.nio.IntBuffer
import java.nio.charset.Charset

object MyGLUtil {  

    fun createSimpleProgram(vertexSourceFilename:String,
                             fragSourceFilename:String,
                             r : Resources) : Int{
        val vShaderSource = readFile(vertexSourceFilename, r)
        val fShaderSource = readFile(fragSourceFilename, r)

        return createSimpleProgram(vShaderSource, fShaderSource)
    }

    fun createSimpleProgram(vertexSource : String, fragSource : String): Int {
        val vShader = loadShader(GLES30.GL_VERTEX_SHADER, vertexSource)
        if(vShader == 0) return 0
        val fShader = loadShader(GLES30.GL_FRAGMENT_SHADER, fragSource)
        if (fShader == 0) return 0

        var program = GLES30.glCreateProgram()
        if(program != 0){
            GLES30.glAttachShader(program, vShader)
            checkGLError("glAttachShader")
            GLES30.glAttachShader(program, fShader)
            checkGLError("glAttachShader")
            GLES30.glLinkProgram(program)

            val info = IntArray(1)
            GLES30.glGetProgramiv(program, GLES30.GL_LINK_STATUS, info, 0)
            if(info[0] != GLES30.GL_TRUE){
                Log.e("GLES30_ERROR", " Could  not link Program: ")
                Log.e("GLES30_ERROR", GLES30.glGetProgramInfoLog(program))
                GLES30.glDeleteProgram(program)
                program = 0
            }
        }
        return  program
    }

    private fun loadShader(shaderType : Int, source : String) : Int{
        var shader = GLES30.glCreateShader(shaderType)
        if(shader != 0) {
            GLES30.glShaderSource(shader,source)
            GLES30.glCompileShader(shader)
            val info = IntArray(1)

            GLES30.glGetShaderiv(shader, GLES30.GL_COMPILE_STATUS, info, 0)
            if(info[0] == 0){
                Log.e("GLES30_ERROR", " Could  not compile shader  $shaderType")
                Log.e("GLES30_ERROR", GLES30.glGetShaderInfoLog(shader))
                GLES30.glDeleteShader(shader)
                shader = 0
            }
        }

        return  shader
    }

    private fun checkGLError(op : String){
        var err : Int = GLES30.glGetError()
        var flag = false
        while (err != GLES30.GL_NO_ERROR){
            flag = true
            Log.e("GLES30_ERROR", "$op GL_ERROR $err")
            err = GLES30.glGetError()
        }
        if(flag) throw RuntimeException("GLES30 Compiler Error")
    }

    fun readFile(filename:String, r : Resources) : String {
        val result : String

        val iStream = r.assets.open(filename)
        var ch = 0
        val oStream = ByteArrayOutputStream()

        ch = iStream.read()
        while (ch != -1){
            oStream.write(ch)
            ch = iStream.read()
        }
        val buf = oStream.toByteArray()

        oStream.close()
        iStream.close()


        result = buf.toString(Charset.defaultCharset())
        result.replace("\r\n", "\n")
        return result
    }

    fun initIntBuffer(array: IntArray) : IntBuffer {
        val mBuffer : IntBuffer
        val byteBuffer = ByteBuffer.allocateDirect(array.size * 4)
        byteBuffer.order(ByteOrder.nativeOrder())
        mBuffer = byteBuffer.asIntBuffer()
        mBuffer.put(array)
        mBuffer.position(0)
        return mBuffer
    }

    fun initFloatBuffer(array: FloatArray) : FloatBuffer {
        val mBuffer : FloatBuffer
        val byteBuffer = ByteBuffer.allocateDirect(array.size * 4)  // GLFloat len is 4b
        byteBuffer.order(ByteOrder.nativeOrder())
        mBuffer = byteBuffer.asFloatBuffer()
        mBuffer.put(array)
        mBuffer.position(0)
        return mBuffer
    }



    fun initFloatBuffer1(vararg elem: Float) : FloatBuffer {
        return initFloatBuffer(elem)
    }

    fun lenForNFloat(n : Int) : Int{
        return 4 * n
    }
}